/*
 * Copyright (C) 2011  Paulo Cesar Pereira de Andrade.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Authors:
 *	Paulo Cesar Pereira de Andrade
 */

#include "exl.h"
#include "ejit.h"
#include "ejit_private.h"

#include <sys/mman.h>

#define jit_regload_reload		0	/* convert to reload */
#define jit_regload_delete		1	/* just remove node */
#define jit_regload_isdead		2	/* delete and unset live bit */

/*
 * Prototypes
 */
#define new_pool()			_new_pool(_jit)
static void
_new_pool(jit_state_t *_jit);

#define new_node(code)			_new_node(_jit, code)
static jit_node_t *
_new_node(jit_state_t *_jit, jit_code_t code);

#define link_node(node)			_link_node(_jit, node)
static inline jit_node_t *
_link_node(jit_state_t *_jit, jit_node_t *node);

#define del_node(prev, node)		_del_node(_jit, prev, node)
static inline void
_del_node(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node);

#define del_label(prev, node)		_del_label(_jit, prev, node)
static void
_del_label(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node);

#define jit_setup(block)		_jit_setup(_jit, block)
static void
_jit_setup(jit_state_t *_jit, jit_block_t *block);

#define jit_update(node, live, mask)	_jit_update(_jit, node, live, mask)
static void
_jit_update(jit_state_t *_jit, jit_node_t *node,
	    jit_regset_t *live, jit_regset_t *mask);

#define thread_jumps()			_thread_jumps(_jit)
static void
_thread_jumps(jit_state_t *_jit);

#define sequential_labels()		_sequential_labels(_jit)
static void
_sequential_labels(jit_state_t *_jit);

#define shortcut_jump(prev, node)	_shortcut_jump(_jit, prev, node)
static jit_bool_t
_shortcut_jump(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node);

#define redundant_jump(prev, node)	_redundant_jump(_jit, prev, node)
static jit_bool_t
_redundant_jump(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node);

static jit_code_t
reverse_jump_code(jit_code_t code);

#define reverse_jump(prev, node)	_reverse_jump(_jit, prev, node)
static jit_bool_t
_reverse_jump(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node);

#define redundant_store(node, jump)	_redundant_store(_jit, node, jump)
static void
_redundant_store(jit_state_t *_jit, jit_node_t *node, jit_bool_t jump);

#define simplify_movr(p, n, k, s)	_simplify_movr(_jit, p, n, k, s)
static jit_bool_t
_simplify_movr(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node,
	       jit_int32_t kind, jit_int32_t size);

#define simplify_movi(p, n, k, s)	_simplify_movi(_jit, p, n, k, s)
static jit_bool_t
_simplify_movi(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node,
	       jit_int32_t kind, jit_int32_t size);

#define simplify_ldxi(prev, node)	_simplify_ldxi(_jit, prev, node)
static jit_bool_t
_simplify_ldxi(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node);

#define simplify_stxi(prev, node)	_simplify_stxi(_jit, prev, node)
static jit_bool_t
_simplify_stxi(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node);

#define simplify_spill(node, regno)	_simplify_spill(_jit, node, regno)
static void
_simplify_spill(jit_state_t *_jit, jit_node_t *node, jit_int32_t regno);

#define simplify()			_simplify(_jit)
static void
_simplify(jit_state_t *_jit);

#define jit_reg_undef			-1
#define jit_reg_static			 0
#define jit_reg_change			 1
#define register_change_p(n, l, r)	_register_change_p(_jit, n, l, r)
static jit_int32_t
_register_change_p(jit_state_t *_jit, jit_node_t *node, jit_node_t *link,
		   jit_int32_t regno);

#define spill_reglive_p(node, regno)	_spill_reglive_p(_jit, node, regno)
static jit_bool_t
_spill_reglive_p(jit_state_t *_jit, jit_node_t *node, jit_int32_t regno);

#define patch_registers()		_patch_registers(_jit)
static void
_patch_registers(jit_state_t *_jit);

#define patch_register(n,l,r,p)		_patch_register(_jit,n,l,r,p)
static void
_patch_register(jit_state_t *jit, jit_node_t *node, jit_node_t *link,
		jit_int32_t regno, jit_int32_t patch);

/*
 * Initialization
 */
#if DEBUG
#  define entry(name)		{ name, sizeof(name) - 1 }
jit_code_info_t	 _jit_code_info[] = {
    entry("data"),		entry("save"),
    entry("load"),		entry("label"),
    entry("#note"),		entry("addr"),
    entry("addi"),		entry("addxr"),
    entry("addxi"),		entry("addcr"),
    entry("addci"),		entry("subr"),
    entry("subi"),		entry("subxr"),
    entry("subxi"),		entry("subcr"),
    entry("subci"),		entry("mulr"),
    entry("muli"),		entry("divr"),
    entry("divi"),		entry("divr_u"),
    entry("divi_u"),		entry("remr"),
    entry("remi"),		entry("remr_u"),
    entry("remi_u"),		entry("andr"),
    entry("andi"),		entry("orr"),
    entry("ori"),		entry("xorr"),
    entry("xori"),		entry("lshr"),
    entry("lshi"),		entry("rshr"),
    entry("rshi"),		entry("rshr_u"),
    entry("rshi_u"),		entry("negr"),
    entry("comr"),		entry("ltr"),
    entry("lti"),		entry("ltr_u"),
    entry("lti_u"),		entry("ler"),
    entry("lei"),		entry("ler_u"),
    entry("lei_u"),		entry("eqr"),
    entry("eqi"),		entry("ger"),
    entry("gei"),		entry("ger_u"),
    entry("gei_u"),		entry("gtr"),
    entry("gti"),		entry("gtr_u"),
    entry("gti_u"),		entry("ner"),
    entry("nei"),		entry("movr"),
    entry("movi"),		entry("extr_c"),
    entry("extr_uc"),		entry("extr_s"),
    entry("extr_us"),		entry("extr_i"),
    entry("extr_ui"),		entry("truncr_f_i"),
    entry("truncr_f_l"),	entry("truncr_d_i"),
    entry("truncr_d_l"),	entry("htonr"),
    entry("ldr_c"),		entry("ldi_c"),
    entry("ldr_uc"),		entry("ldi_uc"),
    entry("ldr_s"),		entry("ldi_s"),
    entry("ldr_us"),		entry("ldi_us"),
    entry("ldr_i"),		entry("ldi_i"),
    entry("ldr_ui"),		entry("ldi_ui"),
    entry("ldr_l"),		entry("ldi_l"),
    entry("ldxr_c"),		entry("ldxi_c"),
    entry("ldxr_uc"),		entry("ldxi_uc"),
    entry("ldxr_s"),		entry("ldxi_s"),
    entry("ldxr_us"),		entry("ldxi_us"),
    entry("ldxr_i"),		entry("ldxi_i"),
    entry("ldxr_ui"),		entry("ldxi_ui"),
    entry("ldxr_l"),		entry("ldxi_l"),
    entry("str_c"),		entry("sti_c"),
    entry("str_s"),		entry("sti_s"),
    entry("str_i"),		entry("sti_i"),
    entry("str_l"),		entry("sti_l"),
    entry("stxr_c"),		entry("stxi_c"),
    entry("stxr_s"),		entry("stxi_s"),
    entry("stxr_i"),		entry("stxi_i"),
    entry("stxr_l"),		entry("stxi_l"),
    entry("bltr"),		entry("blti"),
    entry("bltr_u"),		entry("blti_u"),
    entry("bler"),		entry("blei"),
    entry("bler_u"),		entry("blei_u"),
    entry("beqr"),		entry("beqi"),
    entry("bger"),		entry("bgei"),
    entry("bger_u"),		entry("bgei_u"),
    entry("bgtr"),		entry("bgti"),
    entry("bgtr_u"),		entry("bgti_u"),
    entry("bner"),		entry("bnei"),
    entry("bmsr"),		entry("bmsi"),
    entry("bmcr"),		entry("bmci"),
    entry("boaddr"),		entry("boaddi"),
    entry("boaddr_u"),		entry("boaddi_u"),
    entry("bxaddr"),		entry("bxaddi"),
    entry("bxaddr_u"),		entry("bxaddi_u"),
    entry("bosubr"),		entry("bosubi"),
    entry("bosubr_u"),		entry("bosubi_u"),
    entry("bxsubr"),		entry("bxsubi"),
    entry("bxsubr_u"),		entry("bxsubi_u"),
    entry("addr_f"),		entry("addi_f"),
    entry("subr_f"),		entry("subi_f"),
    entry("mulr_f"),		entry("muli_f"),
    entry("divr_f"),		entry("divi_f"),
    entry("negr_f"),		entry("absr_f"),
    entry("sqrtr_f"),		entry("ltr_f"),
    entry("lti_f"),		entry("ler_f"),
    entry("lei_f"),		entry("eqr_f"),
    entry("eqi_f"),		entry("ger_f"),
    entry("gei_f"),		entry("gtr_f"),
    entry("gti_f"),		entry("ner_f"),
    entry("nei_f"),		entry("unltr_f"),
    entry("unlti_f"),		entry("unler_f"),
    entry("unlei_f"),		entry("uneqr_f"),
    entry("uneqi_f"),		entry("unger_f"),
    entry("ungei_f"),		entry("ungtr_f"),
    entry("ungti_f"),		entry("ltgtr_f"),
    entry("ltgti_f"),		entry("ordr_f"),
    entry("ordi_f"),		entry("unordr_f"),
    entry("unordi_f"),		entry("movr_f"),
    entry("movi_f"),		entry("extr_f"),
    entry("extr_d_f"),		entry("ldr_f"),
    entry("ldi_f"),		entry("ldxr_f"),
    entry("ldxi_f"),		entry("str_f"),
    entry("sti_f"),		entry("stxr_f"),
    entry("stxi_f"),		entry("bltr_f"),
    entry("blti_f"),		entry("bler_f"),
    entry("blei_f"),		entry("beqr_f"),
    entry("beqi_f"),		entry("bger_f"),
    entry("bgei_f"),		entry("bgtr_f"),
    entry("bgti_f"),		entry("bner_f"),
    entry("bnei_f"),		entry("bunltr_f"),
    entry("bunlti_f"),		entry("bunler_f"),
    entry("bunlei_f"),		entry("buneqr_f"),
    entry("buneqi_f"),		entry("bunger_f"),
    entry("bungei_f"),		entry("bungtr_f"),
    entry("bungti_f"),		entry("bltgtr_f"),
    entry("bltgti_f"),		entry("bordr_f"),
    entry("bordi_f"),		entry("bunordr_f"),
    entry("bunordi_f"),		entry("addr_d"),
    entry("addi_d"),		entry("subr_d"),
    entry("subi_d"),		entry("mulr_d"),
    entry("muli_d"),		entry("divr_d"),
    entry("divi_d"),		entry("negr_d"),
    entry("absr_d"),		entry("sqrtr_d"),
    entry("ltr_d"),		entry("lti_d"),
    entry("ler_d"),		entry("lei_d"),
    entry("eqr_d"),		entry("eqi_d"),
    entry("ger_d"),		entry("gei_d"),
    entry("gtr_d"),		entry("gti_d"),
    entry("ner_d"),		entry("nei_d"),
    entry("unltr_d"),		entry("unlti_d"),
    entry("unler_d"),		entry("unlei_d"),
    entry("uneqr_d"),		entry("uneqi_d"),
    entry("unger_d"),		entry("ungei_d"),
    entry("ungtr_d"),		entry("ungti_d"),
    entry("ltgtr_d"),		entry("ltgti_d"),
    entry("ordr_d"),		entry("ordi_d"),
    entry("unordr_d"),		entry("unordi_d"),
    entry("movr_d"),		entry("movi_d"),
    entry("extr_d"),		entry("extr_f_d"),
    entry("ldr_d"),		entry("ldi_d"),
    entry("ldxr_d"),		entry("ldxi_d"),
    entry("str_d"),		entry("sti_d"),
    entry("stxr_d"),		entry("stxi_d"),
    entry("bltr_d"),		entry("blti_d"),
    entry("bler_d"),		entry("blei_d"),
    entry("beqr_d"),		entry("beqi_d"),
    entry("bger_d"),		entry("bgei_d"),
    entry("bgtr_d"),		entry("bgti_d"),
    entry("bner_d"),		entry("bnei_d"),
    entry("bunltr_d"),		entry("bunlti_d"),
    entry("bunler_d"),		entry("bunlei_d"),
    entry("buneqr_d"),		entry("buneqi_d"),
    entry("bunger_d"),		entry("bungei_d"),
    entry("bungtr_d"),		entry("bungti_d"),
    entry("bltgtr_d"),		entry("bltgti_d"),
    entry("bordr_d"),		entry("bordi_d"),
    entry("bunordr_d"),		entry("bunordi_d"),
    entry("callr"),		entry("calli"),
    entry("jmpr"),		entry("jmpi"),
    entry("prolog"),		entry("epilog"),
    entry("retval_f"),		entry("retval_d"),
};
#  undef entry
#endif

/*
 * Implementation
 */
void
init_jit(void)
{
    jit_get_cpu();
}

jit_int32_t
_jit_get_reg(jit_state_t *_jit, jit_int32_t regspec)
{
    jit_int32_t		spec;
    jit_int32_t		regno;

    /* if asking for an explicit register value, assume it will
     * properly handle the case of the register also being an
     * argument for the instruction, or the register value
     * being live */
    spec = regspec & ~(jit_class_chk|jit_class_nospill);
    if ((regno = jit_regno(spec))) {
	if (jit_regset_tstbit(_jit->regsav, regno)) {
	    /* fail if register is spilled */
	    assert(regspec & jit_class_chk);
	    return (JIT_NOREG);
	}
	if (jit_regset_tstbit(_jit->regarg, regno)) {
	    if (regspec & jit_class_nospill) {
		if (regspec & jit_class_chk)
		    return (JIT_NOREG);
		goto fail;
	    }
	    goto spill;
	}
	jit_regset_setbit(_jit->regarg, regno);
	return (regno);
    }
    else
	assert(jit_class(spec) != 0);

    if (_jit->emit) {
	/* search for a free register matching spec */
	for (regno = 0; regno < _jit->reglen; regno++) {
	    if ((jit_class(_rvs[regno].spec) & spec) == spec &&
		!jit_regset_tstbit(_jit->regarg, regno) &&
		!jit_regset_tstbit(_jit->reglive, regno))
		goto regarg;
	}

	/* search for a register matching spec that is not an argument
	 * for the current instruction */
	for (regno = 0; regno < _jit->reglen; regno++) {
	    if ((jit_class(_rvs[regno].spec) & spec) == spec &&
		!jit_regset_tstbit(_jit->regsav, regno) &&
		!jit_regset_tstbit(_jit->regarg, regno) &&
		!(regspec & jit_class_nospill)) {
	    spill:
		assert(_jit->function != null);
		if (spec & jit_class_gpr) {
		    if (!_jit->function->regoff[regno]) {
			_jit->function->regoff[regno] =
			    jit_allocai(sizeof(jit_word_t));
			_jit->again = true;
		    }
		    emit_stxi(_jit->function->regoff[regno], JIT_FP, regno);
		}
		else {
		    if (!_jit->function->regoff[regno]) {
			_jit->function->regoff[regno] =
			    jit_allocai(sizeof(efloat64_t));
			_jit->again = true;
		    }
		    emit_stxi_d(_jit->function->regoff[regno], JIT_FP, regno);
		}
		jit_regset_setbit(_jit->regsav, regno);
	    regarg:
		jit_regset_setbit(_jit->regarg, regno);
		if (jit_class(_rvs[regno].spec) & jit_class_sav) {
		    /* if will modify callee save registers without a
		     * function prolog, better patch this assertion */
		    assert(_jit->function);
		    if (!jit_regset_tstbit(_jit->function->regset, regno)) {
			jit_regset_setbit(_jit->function->regset, regno);
			_jit->again = true;
		    }
		}
		return (regno);
	    }
	}
    }
    else {
	if (regspec & jit_class_nospill)
	    eerror("nospill hint only valid during emit");
	for (regno = 0; regno < _jit->reglen; regno++) {
	    if ((jit_class(_rvs[regno].spec) & spec) == spec &&
		!jit_regset_tstbit(_jit->regsav, regno) &&
		!jit_regset_tstbit(_jit->regarg, regno)) {
		jit_regset_setbit(_jit->regarg, regno);
		jit_regset_setbit(_jit->regsav, regno);
		jit_save(regno);
		return (jit_regno_patch|regno);
	    }
	}
	if (regspec & jit_class_chk)
	    return (JIT_NOREG);
    }

    if (regspec & jit_class_chk)
	return (JIT_NOREG);

fail:
    eerror("out of hardware registers");
}

void
_jit_unget_reg(jit_state_t *_jit, jit_int32_t regno)
{
    regno = jit_regno(regno);
    if (jit_regset_tstbit(_jit->regsav, regno)) {
	if (_jit->emit) {
	    if (jit_class(_rvs[regno].spec) & jit_class_gpr)
		emit_ldxi(regno, JIT_FP, _jit->function->regoff[regno]);
	    else
		emit_ldxi_d(regno, JIT_FP, _jit->function->regoff[regno]);
	}
	else
	    jit_load(regno);
	jit_regset_clrbit(_jit->regsav, regno);
    }
    assert(jit_regset_tstbit(_jit->regarg, regno));
    jit_regset_clrbit(_jit->regarg, regno);
}

#if JIT_REGSET_INT
unsigned long
jit_regset_scan1(jit_regset_t set, jit_int32_t offset)
{
    jit_int32_t		index;
    jit_int32_t		length;
    union {
	jit_uint64_t	ul;
	jit_uint8_t	uc[8];
    } data;

    assert(offset >= 0 && offset <= 63);
    data.ul = set;
    if (data.uc[index = offset >> 3]) {
	length = (index + 1) << 3;
	for (; offset < length; offset++) {
	    if (set & (1LL << offset))
		return (offset);
	}
    }
    for (index++; index < 8; index++) {
	if (data.uc[index]) {
	    offset = index << 3;
	    length = (index + 1) << 3;
	    for (; offset < length; offset++) {
		if (set & (1LL << offset))
		    return (offset);
	    }
	}
    }
    return (ULONG_MAX);
}
#endif

void
_jit_save(jit_state_t *_jit, jit_int32_t reg)
{
    reg = jit_regno(reg);
    assert(!_jit->emit);
    _jit->spill[reg] = jit_new_node_w(jit_code_save, reg);
}

void
_jit_load(jit_state_t *_jit, jit_int32_t reg)
{
    jit_node_t		*node;

    reg = jit_regno(reg);
    assert(!_jit->emit);
    assert(_jit->spill[reg] != null);
    node = jit_new_node_w(jit_code_load, reg);
    /* create a path to flag the save/load is not required */
    node->link = _jit->spill[reg];
    node->link->link = node;
    _jit->spill[reg] = null;
}

jit_node_t *
_jit_data(jit_state_t *_jit, jit_pointer_t data, jit_int32_t length)
{
    jit_node_t		*node;
    eentry_t		*entry;

    assert(_jit->emit == false);
    if ((entry = eget_hash_string(_jit->data_hash, data, length)) == null) {
	enew(&thread_self->obj, entry);
	entry = thread_self->obj;
	/* must be t_resize|t_uint8 for eget_hash_string work as intended */
	enew_object(&entry->name, t_resize | t_uint8, length);
	memcpy(entry->name, data, length);
	eput_hash(_jit->data_hash, entry);
	enew_object(&entry->value, t_jit_node, sizeof(jit_node_t));
	node = entry->value;
	node->code = jit_code_data;
	/* work with jit_node_t object to make automatic patching easier */
	node->u.w = (jit_word_t)entry->name;
	node->v.w = length;
	switch (length) {
	    case 0:	case 1:
		break;
	    case 2:
		_jit->data_length = (_jit->data_length + 1) & -2;
		break;
	    case 3:	case 4:
		_jit->data_length = (_jit->data_length + 3) & -4;
		break;
	    default:
		_jit->data_length = (_jit->data_length + 7) & -8;
		break;
	}
	node->w.w = _jit->data_length;
	_jit->data_length += length;
    }

    return (entry->value);
}

static void
_new_pool(jit_state_t *_jit)
{
    jit_pointer_t	*ptr;
    jit_node_t		*list;
    jit_int32_t		 offset;

    if (_jit->pool->offset >= _jit->pool->length)
	erenew_vector(_jit->pool, _jit->pool->length + 16);
    ptr = _jit->pool->v.obj;
    enew_object(ptr + _jit->pool->offset, t_void, sizeof(jit_node_t) * 1024);

    list = ptr[_jit->pool->offset];
    for (offset = 1; offset < 1024; offset++, list++)
	list->next = list + 1;
    list->next = _jit->list;
    _jit->list = ptr[_jit->pool->offset];
    ++_jit->pool->offset;
}

static jit_node_t *
_new_node(jit_state_t *_jit, jit_code_t code)
{
    jit_node_t		*node;

    if (_jit->list == null)
	new_pool();
    node = _jit->list;
    _jit->list = node->next;
    node->next = null;
    node->code = code;

    return (node);
}

static inline jit_node_t *
_link_node(jit_state_t *_jit, jit_node_t *node)
{
    if (_jit->tail)
	_jit->tail->next = node;
    else
	_jit->head = node;
    return (_jit->tail = node);
}

static inline void
_del_node(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node)
{
    if (prev == node) {
	assert(prev == _jit->head);
	_jit->head = node->next;
    }
    else
	prev->next = node->next;
    memset(node, 0, sizeof(jit_node_t));
    node->next = _jit->list;
    _jit->list = node;
}

static void
_del_label(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node)
{
    jit_block_t		 *block;
    jit_block_t		**blocks;

    /* only allow call to del_label on linked labels */
    blocks = _jit->blocks->v.obj;
    block = blocks[node->v.w];
    assert(block->label == node);
    blocks[node->v.w] = null;
    /* redundant, should be already true */
    assert(node->link == null);
    del_node(prev, node);
}

void
jit_new_state(jit_pointer_t *pointer)
{
    jit_state_t		*_jit;

    enew_object(pointer, t_jit_state, sizeof(jit_state_t));
    _jit = *pointer;
    jit_regset_new(_jit->regarg);
    jit_regset_new(_jit->regsav);
    jit_regset_new(_jit->reglive);
    jit_regset_new(_jit->regmask);
    enew_mpz_u((jit_pointer_t *)&_jit->blockmask, 0);
    enew_hash((jit_pointer_t *)&_jit->data_hash, H_string, 5);
    evector(&_jit->blocks, t_void, 16, 1);
    evector(&_jit->patches, t_word, 4096, 1);
    evector(&_jit->functions, t_void, 16, 1);
    jit_init();
    enew_object((eobject_t *)&_jit->spill, t_void,
		sizeof(jit_pointer_t) * _jit->reglen);
#if JIT_REGSET_INT
    assert((sizeof(jit_regset_t) << 3) >= _jit->reglen);
#endif
    enew_object((eobject_t *)&_jit->gen, t_int32,
		sizeof(jit_int32_t) * _jit->reglen);
    enew_object((eobject_t *)&_jit->values, t_void,
		sizeof(jit_value_t) * _jit->reglen);
    evector(&_jit->pool, t_void, 16, 1);
#if __arm__ && DISASSEMBLER
    if (cfg_verbose > 1)
	evector((jit_pointer_t *)&_jit->data_info, t_word, 1024, 1);
#endif
}

jit_node_t *
_jit_new_node(jit_state_t *_jit, jit_code_t code)
{
    assert(_jit->emit == false);
    return (link_node(new_node(code)));
}

jit_node_t *
_jit_new_node_no_link(jit_state_t *_jit, jit_code_t code)
{
    assert(_jit->emit == false);
    return (new_node(code));
}

void
_jit_link_node(jit_state_t *_jit, jit_node_t *node)
{
    assert(_jit->emit == false);
    link_node(node);
}

jit_node_t *
_jit_new_node_w(jit_state_t *_jit, jit_code_t code,
		jit_word_t u)
{
    jit_node_t		*node = new_node(code);
    assert(_jit->emit == false);
    node->u.w = u;
    return (link_node(node));
}

jit_node_t *
_jit_new_node_p(jit_state_t *_jit, jit_code_t code,
		jit_pointer_t u)
{
    jit_node_t		*node = new_node(code);
    assert(_jit->emit == false);
    node->u.p = u;
    return (link_node(node));
}

jit_node_t *
_jit_new_node_ww(jit_state_t *_jit, jit_code_t code,
		 jit_word_t u, jit_word_t v)
{
    jit_node_t		*node = new_node(code);
    assert(_jit->emit == false);
    node->u.w = u;
    node->v.w = v;
    return (link_node(node));
}

jit_node_t *
_jit_new_node_wf(jit_state_t *_jit, jit_code_t code,
		 jit_word_t u, jit_float32_t v)
{
    jit_node_t		*node = new_node(code);
    assert(_jit->emit == false);
    node->u.w = u;
    node->v.f = v;
    return (link_node(node));
}

jit_node_t *
_jit_new_node_wd(jit_state_t *_jit, jit_code_t code,
		 jit_word_t u, jit_float64_t v)
{
    jit_node_t		*node = new_node(code);
    assert(_jit->emit == false);
    node->u.w = u;
    node->v.d = v;
    return (link_node(node));
}

jit_node_t *
_jit_new_node_www(jit_state_t *_jit, jit_code_t code,
		  jit_word_t u, jit_word_t v, jit_word_t w)
{
    jit_node_t		*node = new_node(code);
    assert(_jit->emit == false);
    node->u.w = u;
    node->v.w = v;
    node->w.w = w;
    return (link_node(node));
}

jit_node_t *
_jit_new_node_wwf(jit_state_t *_jit, jit_code_t code,
		  jit_word_t u, jit_word_t v, jit_float32_t w)
{
    jit_node_t		*node = new_node(code);
    assert(_jit->emit == false);
    node->u.w = u;
    node->v.w = v;
    node->w.f = w;
    return (link_node(node));
}

jit_node_t *
_jit_new_node_wwd(jit_state_t *_jit, jit_code_t code,
		  jit_word_t u, jit_word_t v, jit_float64_t w)
{
    jit_node_t		*node = new_node(code);
    assert(_jit->emit == false);
    node->u.w = u;
    node->v.w = v;
    node->w.d = w;
    return (link_node(node));
}

jit_node_t *
_jit_new_node_pww(jit_state_t *_jit, jit_code_t code,
		  jit_pointer_t u, jit_word_t v, jit_word_t w)
{
    jit_node_t		*node = new_node(code);
    assert(_jit->emit == false);
    node->u.p = u;
    node->v.w = v;
    node->w.w = w;
    return (link_node(node));
}

jit_node_t *
_jit_new_node_pwf(jit_state_t *_jit, jit_code_t code,
		  jit_pointer_t u, jit_word_t v, jit_float32_t w)
{
    jit_node_t		*node = new_node(code);
    assert(_jit->emit == false);
    node->u.p = u;
    node->v.w = v;
    node->w.f = w;
    return (link_node(node));
}

jit_node_t *
_jit_new_node_pwd(jit_state_t *_jit, jit_code_t code,
		  jit_pointer_t u, jit_word_t v, jit_float64_t w)
{
    jit_node_t		*node = new_node(code);
    assert(_jit->emit == false);
    node->u.p = u;
    node->v.w = v;
    node->w.d = w;
    return (link_node(node));
}

jit_node_t *
_jit_label(jit_state_t *_jit)
{
    jit_node_t		*node;

    if ((node = _jit->tail) == null || node->code != jit_code_label) {
	node = jit_forward();
	jit_link(node);
    }

    return (node);
}

jit_node_t *
_jit_forward(jit_state_t *_jit)
{
    return (jit_new_node_no_link(jit_code_label));
}

void
_jit_link(jit_state_t *_jit, jit_node_t *node)
{
    jit_block_t		 *block;
    jit_block_t		**blocks;

    assert((node->code == jit_code_label ||
	    node->code == jit_code_prolog ||
	    node->code == jit_code_epilog) && node->next == null);
    jit_link_node(node);
    if (_jit->blocks->offset >= _jit->blocks->length)
	erenew_vector(_jit->blocks, _jit->blocks->length + 16);
    blocks = _jit->blocks->v.obj;
    enew_object((jit_pointer_t *)(blocks + _jit->blocks->offset),
		t_jit_block, sizeof(jit_block_t));
    block = blocks[_jit->blocks->offset];
    block->label = node;
    node->v.w = _jit->blocks->offset;
    jit_regset_new(block->reglive);
    jit_regset_new(block->regmask);
    ++_jit->blocks->offset;
}

void
_jit_prepare(jit_state_t *_jit, jit_int32_t kind)
{
    assert(_jit->function != null);
    _jit->function->call.kind = kind;
    _jit->function->call.argi =
	_jit->function->call.argf =
	_jit->function->call.size = 0;
}

void
_jit_patch(jit_state_t* _jit, jit_node_t *instr)
{
    jit_node_t		*label;

    if ((label = _jit->tail) == null ||
	(label->code != jit_code_label && label->code != jit_code_epilog))
	label = jit_label();
    jit_patch_at(instr, label);
}

jit_int32_t
_jit_classify(jit_state_t *_jit, jit_code_t code)
{
    jit_int32_t		mask;

    switch (code) {
	case jit_code_data:	case jit_code_save:	case jit_code_load:
	case jit_code_label:	case jit_code_note:	case jit_code_prolog:
	case jit_code_epilog:
	    mask = 0;
	    break;
	case jit_code_calli:	case jit_code_jmpi:
	    mask = jit_cc_a0_jmp;
	    break;
	case jit_code_callr:	case jit_code_jmpr:
	    mask = jit_cc_a0_reg|jit_cc_a0_jmp;
	    break;
	case jit_code_retval_f:	case jit_code_retval_d:
	    mask = jit_cc_a0_reg|jit_cc_a0_chg;
	    break;
	case jit_code_movi:	case jit_code_ldi_c:	case jit_code_ldi_uc:
	case jit_code_ldi_s:	case jit_code_ldi_us:	case jit_code_ldi_i:
	case jit_code_ldi_ui:	case jit_code_ldi_l:	case jit_code_ldi_f:
	case jit_code_ldi_d:
	    mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_int;
	    break;
	case jit_code_movi_f:
	    mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_flt;
	    break;
	case jit_code_movi_d:
	    mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_dbl;
	    break;
	case jit_code_negr:	case jit_code_comr:	case jit_code_movr:
	case jit_code_extr_c:	case jit_code_extr_uc:	case jit_code_extr_s:
	case jit_code_extr_us:	case jit_code_extr_i:	case jit_code_extr_ui:
	case jit_code_truncr_f_i:			case jit_code_truncr_f_l:
	case jit_code_truncr_d_i:			case jit_code_truncr_d_l:
	case jit_code_htonr:	case jit_code_ldr_c:	case jit_code_ldr_uc:
	case jit_code_ldr_s:	case jit_code_ldr_us:	case jit_code_ldr_i:
	case jit_code_ldr_ui:	case jit_code_ldr_l:	case jit_code_negr_f:
	case jit_code_absr_f:	case jit_code_sqrtr_f:	case jit_code_movr_f:
	case jit_code_extr_f:	case jit_code_extr_d_f:	case jit_code_ldr_f:
	case jit_code_negr_d:	case jit_code_absr_d:	case jit_code_sqrtr_d:
	case jit_code_movr_d:	case jit_code_extr_d:	case jit_code_extr_f_d:
	case jit_code_ldr_d:
	    mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_reg;
	    break;
	case jit_code_addi:	case jit_code_addxi:	case jit_code_addci:
	case jit_code_subi:	case jit_code_subxi:	case jit_code_subci:
	case jit_code_muli:	case jit_code_divi:	case jit_code_divi_u:
	case jit_code_remi:	case jit_code_remi_u:	case jit_code_andi:
	case jit_code_ori:	case jit_code_xori:	case jit_code_lshi:
	case jit_code_rshi:	case jit_code_rshi_u:	case jit_code_lti:
	case jit_code_lti_u:	case jit_code_lei:	case jit_code_lei_u:
	case jit_code_eqi:	case jit_code_gei:	case jit_code_gei_u:
	case jit_code_gti:	case jit_code_gti_u:	case jit_code_nei:
	case jit_code_ldxi_c:	case jit_code_ldxi_uc:	case jit_code_ldxi_s:
	case jit_code_ldxi_us:	case jit_code_ldxi_i:	case jit_code_ldxi_ui:
	case jit_code_ldxi_l:	case jit_code_ldxi_f:	case jit_code_ldxi_d:
	    mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_reg|jit_cc_a2_int;
	    break;
	case jit_code_addi_f:	case jit_code_subi_f:	case jit_code_muli_f:
	case jit_code_divi_f:	case jit_code_lti_f:	case jit_code_lei_f:
	case jit_code_eqi_f:	case jit_code_gei_f:	case jit_code_gti_f:
	case jit_code_nei_f:	case jit_code_unlti_f:	case jit_code_unlei_f:
	case jit_code_uneqi_f:	case jit_code_ungei_f:	case jit_code_ungti_f:
	case jit_code_ltgti_f:	case jit_code_ordi_f:	case jit_code_unordi_f:
	    mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_reg|jit_cc_a2_flt;
	    break;
	case jit_code_addi_d:	case jit_code_subi_d:	case jit_code_muli_d:
	case jit_code_divi_d:	case jit_code_lti_d:	case jit_code_lei_d:
	case jit_code_eqi_d:	case jit_code_gei_d:	case jit_code_gti_d:
	case jit_code_nei_d:	case jit_code_unlti_d:	case jit_code_unlei_d:
	case jit_code_uneqi_d:	case jit_code_ungei_d:	case jit_code_ungti_d:
	case jit_code_ltgti_d:	case jit_code_ordi_d:	case jit_code_unordi_d:
	    mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_reg|jit_cc_a2_dbl;
	    break;
	case jit_code_addr:	case jit_code_addxr:	case jit_code_addcr:
	case jit_code_subr:	case jit_code_subxr:	case jit_code_subcr:
	case jit_code_mulr:	case jit_code_divr:	case jit_code_divr_u:
	case jit_code_remr:	case jit_code_remr_u:	case jit_code_andr:
	case jit_code_orr:	case jit_code_xorr:	case jit_code_lshr:
	case jit_code_rshr:	case jit_code_rshr_u:	case jit_code_ltr:
	case jit_code_ltr_u:	case jit_code_ler:	case jit_code_ler_u:
	case jit_code_eqr:	case jit_code_ger:	case jit_code_ger_u:
	case jit_code_gtr:	case jit_code_gtr_u:	case jit_code_ner:
	case jit_code_ldxr_c:	case jit_code_ldxr_uc:	case jit_code_ldxr_s:
	case jit_code_ldxr_us:	case jit_code_ldxr_i:	case jit_code_ldxr_ui:
	case jit_code_ldxr_l:	case jit_code_addr_f:	case jit_code_subr_f:
	case jit_code_mulr_f:	case jit_code_divr_f:	case jit_code_ltr_f:
	case jit_code_ler_f:	case jit_code_eqr_f:	case jit_code_ger_f:
	case jit_code_gtr_f:	case jit_code_ner_f:	case jit_code_unltr_f:
	case jit_code_unler_f:	case jit_code_uneqr_f:	case jit_code_unger_f:
	case jit_code_ungtr_f:	case jit_code_ltgtr_f:	case jit_code_ordr_f:
	case jit_code_unordr_f:	case jit_code_ldxr_f:	case jit_code_addr_d:
	case jit_code_subr_d:	case jit_code_mulr_d:	case jit_code_divr_d:
	case jit_code_ltr_d:	case jit_code_ler_d:	case jit_code_eqr_d:
	case jit_code_ger_d:	case jit_code_gtr_d:	case jit_code_ner_d:
	case jit_code_unltr_d:	case jit_code_unler_d:	case jit_code_uneqr_d:
	case jit_code_unger_d:	case jit_code_ungtr_d:	case jit_code_ltgtr_d:
	case jit_code_ordr_d:	case jit_code_unordr_d:	case jit_code_ldxr_d:
	    mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_reg|jit_cc_a2_reg;
	    break;
	case jit_code_sti_c:	case jit_code_sti_s:	case jit_code_sti_i:
	case jit_code_sti_l:	case jit_code_sti_f:	case jit_code_sti_d:
	    mask = jit_cc_a0_int|jit_cc_a1_reg;
	    break;
	case jit_code_blti:	case jit_code_blti_u:	case jit_code_blei:
	case jit_code_blei_u:	case jit_code_beqi:	case jit_code_bgei:
	case jit_code_bgei_u:	case jit_code_bgti:	case jit_code_bgti_u:
	case jit_code_bnei:	case jit_code_bmsi:	case jit_code_bmci:
	    mask = jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a2_int;
	    break;
	case jit_code_blti_f:	case jit_code_blei_f:	case jit_code_beqi_f:
	case jit_code_bgei_f:	case jit_code_bgti_f:	case jit_code_bnei_f:
	case jit_code_bunlti_f:	case jit_code_bunlei_f:	case jit_code_buneqi_f:
	case jit_code_bungei_f:	case jit_code_bungti_f:	case jit_code_bltgti_f:
	case jit_code_bordi_f:	case jit_code_bunordi_f:
	    mask = jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a2_flt;
	    break;
	case jit_code_blti_d:	case jit_code_blei_d:	case jit_code_beqi_d:
	case jit_code_bgei_d:	case jit_code_bgti_d:	case jit_code_bnei_d:
	case jit_code_bunlti_d:	case jit_code_bunlei_d:	case jit_code_buneqi_d:
	case jit_code_bungei_d:	case jit_code_bungti_d:	case jit_code_bltgti_d:
	case jit_code_bordi_d:	case jit_code_bunordi_d:
	    mask = jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a2_dbl;
	    break;
	case jit_code_str_c:	case jit_code_str_s:	case jit_code_str_i:
	case jit_code_str_l:	case jit_code_str_f:	case jit_code_str_d:
	    mask = jit_cc_a0_reg|jit_cc_a1_reg;
	    break;
	case jit_code_stxi_c:	case jit_code_stxi_s:	case jit_code_stxi_i:
	case jit_code_stxi_l:	case jit_code_stxi_f:	case jit_code_stxi_d:
	    mask = jit_cc_a0_int|jit_cc_a1_reg|jit_cc_a2_reg;
	    break;
	case jit_code_bltr:	case jit_code_bltr_u:	case jit_code_bler:
	case jit_code_bler_u:	case jit_code_beqr:	case jit_code_bger:
	case jit_code_bger_u:	case jit_code_bgtr:	case jit_code_bgtr_u:
	case jit_code_bner:	case jit_code_bmsr:	case jit_code_bmcr:
	case jit_code_bltr_f:	case jit_code_bler_f:	case jit_code_beqr_f:
	case jit_code_bger_f:	case jit_code_bgtr_f:	case jit_code_bner_f:
	case jit_code_bunltr_f:	case jit_code_bunler_f:	case jit_code_buneqr_f:
	case jit_code_bunger_f:	case jit_code_bungtr_f:	case jit_code_bltgtr_f:
	case jit_code_bordr_f:	case jit_code_bunordr_f:case jit_code_bltr_d:
	case jit_code_bler_d:	case jit_code_beqr_d:	case jit_code_bger_d:
	case jit_code_bgtr_d:	case jit_code_bner_d:	case jit_code_bunltr_d:
	case jit_code_bunler_d:	case jit_code_buneqr_d:	case jit_code_bunger_d:
	case jit_code_bungtr_d:	case jit_code_bltgtr_d:	case jit_code_bordr_d:
	case jit_code_bunordr_d:
	    mask = jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a2_reg;
	    break;
	case jit_code_boaddi:	case jit_code_boaddi_u:	case jit_code_bxaddi:
	case jit_code_bxaddi_u:	case jit_code_bosubi:	case jit_code_bosubi_u:
	case jit_code_bxsubi:	case jit_code_bxsubi_u:
	    mask = jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a1_chg|jit_cc_a2_int;
	    break;
	case jit_code_stxr_c:	case jit_code_stxr_s:	case jit_code_stxr_i:
	case jit_code_stxr_l:	case jit_code_stxr_f:	case jit_code_stxr_d:
	    mask = jit_cc_a0_reg|jit_cc_a1_reg|jit_cc_a2_reg;
	    break;
	case jit_code_boaddr:	case jit_code_boaddr_u:	case jit_code_bxaddr:
	case jit_code_bxaddr_u:	case jit_code_bosubr:	case jit_code_bosubr_u:
	case jit_code_bxsubr:	case jit_code_bxsubr_u:
	    mask = jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a1_chg|jit_cc_a2_reg;
	    break;
	default:
	    abort();
    }

    return (mask);
}

void
_jit_patch_abs(jit_state_t *_jit, jit_node_t *instr, jit_pointer_t address)
{
    jit_int32_t		mask;

    if (instr->code == jit_code_movi)
	instr->v.p = address;
    else {
	mask = jit_classify(instr->code);
	if ((mask & (jit_cc_a0_reg|jit_cc_a0_jmp)) == jit_cc_a0_jmp)
	    instr->u.p = address;
	else
	    eerror("unsupported patch");
    }
}

void
_jit_patch_at(jit_state_t *_jit, jit_node_t *instr, jit_node_t *label)
{
    jit_int32_t		mask;

    instr->flag |= jit_flag_node;
    switch (instr->code) {
	case jit_code_movi:
	    assert(label->code == jit_code_label ||
		   label->code == jit_code_data);
	    instr->v.n = label;
	    if (label->code == jit_code_data)
		instr->flag |= jit_flag_data;
	    break;
	case jit_code_jmpi:
	    assert(label->code == jit_code_label ||
		   label->code == jit_code_epilog);
	    instr->u.n = label;
	    break;
	default:
	    mask = jit_classify(instr->code);
	    if ((mask & (jit_cc_a0_reg|jit_cc_a0_jmp)) == jit_cc_a0_jmp) {
		assert(label->code == jit_code_label);
		instr->u.n = label;
	    }
	    else
		eerror("unsupported patch");
	    break;
    }
    /* link field is used as list of nodes associated with a given label */
    instr->link = label->link;
    label->link = instr;
}

void
_jit_optimize(jit_state_t *_jit)
{
    jit_bool_t		  jump;
    jit_int32_t		  mask;
    jit_node_t		 *node;
    jit_block_t		 *block;
#if JIT_HASH_CONSTS
    eentry_t		 *entry;
#endif
    jit_int32_t		  offset;
    jit_block_t		**blocks;
    jit_pointer_t	 *functions;

    _jit->function = null;
    functions = _jit->functions->v.obj;

    thread_jumps();
    sequential_labels();
    simplify();

    /* create initial mapping of live register values
     * at the start of a basic block */
    blocks = _jit->blocks->v.obj;
    for (offset = 0; offset < _jit->blocks->offset; offset++) {
	if ((block = blocks[offset]) == null)
	    continue;
	if (block->label->code != jit_code_epilog)
	    jit_setup(block);
    }
    /* call jit_update resolving undefined values in reverse
     * order so that sequential code would find most data already
     * resolved when reaching the start of a new basic block */
    for (offset = _jit->blocks->offset -1; offset >= 0; offset--) {
	if ((block = blocks[offset]) == null)
	    continue;
	if (block->label->code != jit_code_epilog) {
	    jit_regset_set(_jit->regmask, block->regmask);
	    mpz_set_ui(_jit->blockmask, 1);
	    jit_update(block->label->next, &block->reglive, &_jit->regmask);
	}
    }

    patch_registers();

    /* figure out labels that are only reached with a jump
     * and is required to do a simple redundant_store removal
     * on jit_beqi below */
    jump = true;
    for (node = _jit->head; node; node = node->next) {
	switch (node->code) {
	    case jit_code_label:
		if (jump == false)
		    node->flag |= jit_flag_head;
		break;
	    case jit_code_jmpi:		case jit_code_jmpr:
	    case jit_code_epilog:
		jump = true;
		break;
	    case jit_code_data:		case jit_code_note:
		break;
	    default:
		jump = false;
		break;
	}
    }

    for (node = _jit->head; node; node = node->next) {
	switch (node->code) {
	    case jit_code_prolog:
		_jit->function = functions[node->w.w];
		break;
	    case jit_code_epilog:
		_jit->function = null;
		break;
	    case jit_code_beqi:
		redundant_store(node, true);
		break;
	    case jit_code_bnei:
		redundant_store(node, false);
		break;
	    default:
		mask = jit_classify(node->code);
#if JIT_HASH_CONSTS
		if (mask & jit_cc_a1_flt) {
		    node->v.p = jit_data(&node->v.f, sizeof(float));
		    node->flag |= jit_flag_node | jit_flag_data;
		}
		else if (mask & jit_cc_a1_dbl) {
		    node->v.p = jit_data(&node->v.d, sizeof(double));
		    node->flag |= jit_flag_node | jit_flag_data;
		}
		else if (mask & jit_cc_a2_flt) {
		    node->w.p = jit_data(&node->w.f, sizeof(float));
		    node->flag |= jit_flag_node | jit_flag_data;
		}
		else if (mask & jit_cc_a2_dbl) {
		    node->w.p = jit_data(&node->w.d, sizeof(double));
		    node->flag |= jit_flag_node | jit_flag_data;
		}
#endif
		if (_jit->function) {
		    if ((mask & (jit_cc_a0_reg|jit_cc_a0_chg)) ==
			(jit_cc_a0_reg|jit_cc_a0_chg))
			jit_regset_setbit(_jit->function->regset,
					  jit_regno(node->u.w));
		    if ((mask & (jit_cc_a1_reg|jit_cc_a1_chg)) ==
			(jit_cc_a1_reg|jit_cc_a1_chg))
			jit_regset_setbit(_jit->function->regset,
					  jit_regno(node->v.w));
		}
		break;
	}
    }

#if JIT_HASH_CONSTS
    /* create read only data buffer */
    if ((_jit->data_length = (_jit->data_length + 4095) & -4096)) {
	_jit->data = mmap(null, _jit->data_length,
			  PROT_READ | PROT_WRITE,
			  MAP_PRIVATE | MAP_ANON, -1, 0);
	if (_jit->data == MAP_FAILED)
	    eerror("mmap failed (%s)", strerror(errno));
	for (offset = 0; offset < _jit->data_hash->size; offset++) {
	    for (entry = _jit->data_hash->entries[offset];
		 entry; entry = entry->next) {
		node = entry->value;
		node->flag |= jit_flag_patch;
		memcpy(_jit->data + node->w.w, (jit_pointer_t)node->u.w,
		       node->v.w);
		node->u.w = (jit_word_t)(_jit->data + node->w.w);
	    }
	}
    }
#endif
}

void
_jit_reglive(jit_state_t *_jit, jit_node_t *node)
{
    jit_int32_t		  spec;
    jit_int32_t		  value;
    jit_block_t		 *block;
    jit_block_t		**blocks;

    switch (node->code) {
	case jit_code_label:	case jit_code_prolog:	case jit_code_epilog:
	    blocks = _jit->blocks->v.obj;
	    block = blocks[node->v.w];
	    jit_regset_set(_jit->reglive, block->reglive);
	    break;
	case jit_code_callr:
	    value = jit_regno(node->u.w);
	    if (!(node->u.w & jit_regno_patch)) {
		jit_regset_setbit(_jit->reglive, value);
	    }
	case jit_code_calli:
	    for (value = 0; value < _jit->reglen; value++) {
		spec = jit_class(_rvs[value].spec);
		if ((spec & jit_class_arg) && jit_regarg_p(node, value))
		    jit_regset_setbit(_jit->reglive, value);
		else if (!(spec & jit_class_sav))
		    jit_regset_clrbit(_jit->reglive, value);
	    }
#if defined(JIT_RET)
	    /* Explicitly set return registers as live, because retval
	     * should be free to not create a note, and/or user not
	     * call jit_retval (but not a good idea to expect JIT_R0
	     * to match JIT_RET) */
	    jit_regset_setbit(_jit->reglive, JIT_RET);
#  if __arm__
	    /* FIXME need a better logic (and r2-r3 may contain results) */
	    jit_regset_setbit(_jit->reglive, _R1);
#  endif
#endif
#if defined(JIT_FRET)
	    jit_regset_setbit(_jit->reglive, JIT_FRET);
#endif
	    break;
	default:
	    value = jit_classify(node->code);
	    if ((value & jit_cc_a0_reg) && !(node->u.w & jit_regno_patch)) {
		if (value & jit_cc_a0_chg) {
		    jit_regset_clrbit(_jit->reglive, node->u.w);
		    jit_regset_setbit(_jit->regmask, node->u.w);
		}
		else
		    jit_regset_setbit(_jit->reglive, node->u.w);
	    }
	    if ((value & jit_cc_a1_reg) && !(node->v.w & jit_regno_patch)) {
		if (value & jit_cc_a1_chg) {
		    jit_regset_clrbit(_jit->reglive, node->v.w);
		    jit_regset_setbit(_jit->regmask, node->v.w);
		}
		else
		    jit_regset_setbit(_jit->reglive, node->v.w);
	    }
	    if ((value & jit_cc_a2_reg) && !(node->w.w & jit_regno_patch))
		jit_regset_setbit(_jit->reglive, node->w.w);
	    if (jit_regset_set_p(_jit->regmask)) {
		mpz_set_ui(_jit->blockmask, 0);
		jit_update(node->next, &_jit->reglive, &_jit->regmask);
		if (jit_regset_set_p(_jit->regmask)) {
		    /* any unresolved live state is considered as live */
		    jit_regset_ior(_jit->reglive, _jit->reglive, _jit->regmask);
		    jit_regset_set_ui(_jit->regmask, 0);
		}
	    }
	    break;
    }
}

void
_jit_regarg_set(jit_state_t *_jit, jit_node_t *node, jit_int32_t value)
{
    if (value & jit_cc_a0_reg)
	jit_regset_setbit(_jit->regarg, jit_regno(node->u.w));
    if (value & jit_cc_a1_reg)
	jit_regset_setbit(_jit->regarg, jit_regno(node->v.w));
    if (value & jit_cc_a2_reg)
	jit_regset_setbit(_jit->regarg, jit_regno(node->w.w));
}

void
_jit_regarg_clr(jit_state_t *_jit, jit_node_t *node, jit_int32_t value)
{
    if (value & jit_cc_a0_reg)
	jit_regset_clrbit(_jit->regarg, jit_regno(node->u.w));
    if (value & jit_cc_a1_reg)
	jit_regset_clrbit(_jit->regarg, jit_regno(node->v.w));
    if (value & jit_cc_a2_reg)
	jit_regset_clrbit(_jit->regarg, jit_regno(node->w.w));
}

/* Compute initial reglive set of a basic block, keeping values not
 * known in the regmask set.
 */
static void
_jit_setup(jit_state_t *_jit, jit_block_t *block)
{
#define reglive			block->reglive
#define regmask			block->regmask
    jit_node_t		*node;
    jit_int32_t		 spec;
    unsigned long	 value;

#if JIT_REGSET_INT
    regmask = (1LL << _jit->reglen) - 1;
#else
    jit_regset_set_ui(regmask, 1);
    mpz_mul_2exp(regmask, regmask, _jit->reglen);
    mpz_sub_ui(regmask, regmask, 1);
#endif
    for (node = block->label->next; node; node = node->next) {
	switch (node->code) {
	    case jit_code_label:	case jit_code_prolog:
	    case jit_code_epilog:
		return;
	    case jit_code_callr:
		if (!(node->u.w & jit_regno_patch) &&
		    jit_regset_tstbit(regmask, node->u.w)) {
		    jit_regset_clrbit(regmask, node->u.w);
		    jit_regset_setbit(reglive, node->u.w);
		}
	    case jit_code_calli:
		for (value = jit_regset_scan1(regmask, 0); value != ULONG_MAX;
		     value = jit_regset_scan1(regmask, value + 1)) {
		    spec = jit_class(_rvs[value].spec);
		    if (!(spec & jit_class_sav))
			jit_regset_clrbit(regmask, value);
		    if ((spec & jit_class_arg) && jit_regarg_p(node, value))
			jit_regset_setbit(reglive, value);
		}
		/* If result not already marked as live, record it may
		 * be used, so that subsequent call to jit_update
		 * will verify it. */
#if defined(JIT_RET)
		if (!jit_regset_tstbit(reglive, JIT_RET))
		    jit_regset_setbit(regmask, JIT_RET);
#  if __arm__
		if (!jit_regset_tstbit(reglive, _R1))
		    jit_regset_setbit(regmask, _R1);
#  endif
#endif
#if defined(JIT_FRET)
		if (!jit_regset_tstbit(reglive, JIT_FRET))
		    jit_regset_setbit(regmask, JIT_FRET);
#endif
		    break;
	    default:
		value = jit_classify(node->code);
		if ((value & jit_cc_a0_reg) &&
		    !(node->u.w & jit_regno_patch) &&
		    jit_regset_tstbit(regmask, node->u.w)) {
		    jit_regset_clrbit(regmask, node->u.w);
		    if (!(value & jit_cc_a0_chg))
			jit_regset_setbit(reglive, node->u.w);
		}
		if ((value & jit_cc_a1_reg) &&
		    !(node->v.w & jit_regno_patch) &&
		    jit_regset_tstbit(regmask, node->v.w)) {
		    jit_regset_clrbit(regmask, node->v.w);
		    if (!(value & jit_cc_a1_chg))
			jit_regset_setbit(reglive, node->v.w);
		}
		if ((value & jit_cc_a2_reg) &&
		    !(node->w.w & jit_regno_patch) &&
		    jit_regset_tstbit(regmask, node->w.w)) {
		    jit_regset_clrbit(regmask, node->w.w);
		    jit_regset_setbit(reglive, node->w.w);
		}
		break;
	}
    }
#undef regmask
#undef reglive
}

/* remove bit of mask argument based on instructions arguments up to end
 * of code or finding a basic block boundary, if a value is used as argument,
 * also set the live bit to known value cannot be clobbered; if value is
 * modified, just remove it from the mask as if it not already in the live
 * bitmask, then the value is dead
 * FIXME it should really stop on basic block boundaries, but for now
 * at least, keep parsing nodes to avoid incorrectly deciding a register
 * is not live, in case the initial live state is not consistent (changes
 * caused by temporary register allocation should not cross basic block
 * boundaries, so, initial computation by jit_setup+jit_update should
 * hold up to end of jit generation, but, some things like simplify()
 * or any other kind of register patching may have side effects that
 * could only be properly handled by doing a second jit_setup+jit_update
 * sequence, what is not cheap...)
 */
static void
_jit_update(jit_state_t *_jit, jit_node_t *node,
	    jit_regset_t *live, jit_regset_t *mask)
{
    jit_int32_t		  spec;
#if JIT_REGSET_INT
    jit_regset_t	  ztmp;
#else
#  define ztmp		  thr_zr
    evalue_t		 *stack;
#endif
    jit_regset_t	  zmask;
    unsigned long	  value;
    jit_block_t		 *block;
    jit_node_t		 *label;
    jit_block_t		**blocks;

    for (; node; node = node->next) {
    restart:
	if (jit_regset_set_p(*mask) == 0)
	    break;
	switch (node->code) {
	    case jit_code_label:
		blocks = _jit->blocks->v.obj;
		block = blocks[node->v.w];
		if (mpz_tstbit(_jit->blockmask, node->v.w))
		    return;
		mpz_setbit(_jit->blockmask, node->v.w);
		jit_regset_and(ztmp, *mask, block->reglive);
		if (jit_regset_set_p(ztmp)) {
		    jit_regset_ior(*live, *live, ztmp);
		    jit_regset_com(ztmp, ztmp);
		    jit_regset_and(*mask, *mask, ztmp);
		}
		break;
	    case jit_code_prolog:	case jit_code_epilog:
		jit_regset_set_ui(*mask, 0);
		return;
	    case jit_code_callr:
		value = jit_regno(node->u.w);
		if (!(node->u.w & jit_regno_patch)) {
		    if (jit_regset_tstbit(*mask, value)) {
			jit_regset_clrbit(*mask, value);
			jit_regset_setbit(*live, value);
		    }
		}
	    case jit_code_calli:
#if defined(JIT_RET)
		if (jit_regset_tstbit(*mask, JIT_RET)) {
		    jit_regset_setbit(_jit->reglive, JIT_RET);
		    jit_regset_clrbit(*mask, JIT_RET);
		}
#  if __arm__
		if (jit_regset_tstbit(*mask, _R1)) {
		    jit_regset_setbit(_jit->reglive, _R1);
		    jit_regset_clrbit(*mask, _R1);
		}
#  endif
#endif
#if defined(JIT_FRET)
		if (jit_regset_tstbit(*mask, JIT_FRET)) {
		    jit_regset_setbit(_jit->reglive, JIT_FRET);
		    jit_regset_clrbit(*mask, JIT_FRET);
		}
#endif
		for (value = jit_regset_scan1(*mask, 0); value != ULONG_MAX;
		     value = jit_regset_scan1(*mask, value + 1)) {
		    spec = jit_class(_rvs[value].spec);
		    if (!(spec & jit_class_sav))
			jit_regset_clrbit(*mask, value);
		    if ((spec & jit_class_arg) && jit_regarg_p(node, value))
			jit_regset_setbit(*live, value);
		}
		break;
	    default:
		value = jit_classify(node->code);
		if (value & jit_cc_a2_reg) {
		    if (!(node->w.w & jit_regno_patch)) {
			if (jit_regset_tstbit(*mask, node->w.w))
			    jit_regset_clrbit(*mask, node->w.w);
				jit_regset_setbit(*live, node->w.w);
		    }
		}
		if (value & jit_cc_a1_reg) {
		    if (!(node->v.w & jit_regno_patch)) {
			if (jit_regset_tstbit(*mask, node->v.w)) {
			    jit_regset_clrbit(*mask, node->v.w);
			    if (!(value & jit_cc_a1_chg))
				jit_regset_setbit(*live, node->v.w);
			}
		    }
		}
		if (value & jit_cc_a0_reg) {
		    if (!(node->u.w & jit_regno_patch)) {
			if (jit_regset_tstbit(*mask, node->u.w)) {
			    jit_regset_clrbit(*mask, node->u.w);
			    if (!(value & jit_cc_a0_chg))
				jit_regset_setbit(*live, node->u.w);
			}
		    }
		}
		if (value & jit_cc_a0_jmp) {
		    if (node->flag & jit_flag_node) {
			label = node->u.n;
			if (node->code == jit_code_jmpi) {
			    node = label;
			    goto restart;
			}
			if (label->code == jit_code_label) {
			    blocks = _jit->blocks->v.obj;
			    block = blocks[label->v.w];
			    if (mpz_tstbit(_jit->blockmask, label->v.w))
				continue;
			    mpz_setbit(_jit->blockmask, label->v.w);
			    jit_regset_and(ztmp, *mask, block->reglive);
			    if (jit_regset_set_p(ztmp)) {
				jit_regset_ior(*live, *live, ztmp);
				jit_regset_com(ztmp, ztmp);
				jit_regset_and(*mask, *mask, ztmp);
			    }
			    if (jit_regset_set_p(*mask) == 0)
				return;
			    /* restore mask if branch is conditional */
#if JIT_REGSET_MPZ
			    stack = null;
			    if (node->code != jit_code_jmpi) {
				if (thread_self->esp -
				    thread_self->ess >= thread_self->len)
				    evm_stack_resize(8192);
				stack = thread_self->esp++;
				if (thread_self->esp[-1].t != t_shz)
				    eget_mpz(*mask, (thread_self->esp -
						    thread_self->ess) - 1);
				else
				    jit_regset_set(thread_self->esp[-1].v.mpz,
						   *mask);
				zmask = thread_self->esp[-1].v.mpz;
			    }
			    else
				zmask = *mask;
#else
			    zmask = *mask;
#endif
			    jit_update(block->label->next, live, &zmask);
			    jit_regset_xor(ztmp, zmask, *mask);
			    /* remove known live registers from mask */
			    if (jit_regset_set_p(ztmp)) {
				jit_regset_and(ztmp, ztmp, *live);
				jit_regset_com(ztmp, ztmp);
				jit_regset_and(*mask, *mask, ztmp);
			    }
#if JIT_REGSET_MPZ
			    thread_self->esp = stack;
#endif
			    continue;
			}
		    }
		    /* assume value is live due to jump to unknown location */
		    jit_regset_ior(*live, *live, *mask);
		    jit_regset_set_ui(*mask, 0);
		    return;
		}
		break;
	}
    }
}

static void
_thread_jumps(jit_state_t *_jit)
{
    jit_node_t		*prev;
    jit_node_t		*node;
    jit_node_t		*next;
    jit_int32_t		 mask;

    for (prev = node = _jit->head; node;) {
	next = node->next;
	switch (node->code) {
	    case jit_code_jmpi:
		if (redundant_jump(prev, node) ||
		    shortcut_jump(prev, node))
		    continue;
		break;
	    case jit_code_jmpr:
		if (shortcut_jump(prev, node))
		    continue;
		break;
	    case jit_code_callr:	case jit_code_calli:
		/* non optimizable jump like code */
		break;
	    default:
		mask = jit_classify(node->code);
		if (mask & jit_cc_a0_jmp) {
		    if (reverse_jump(prev, node) ||
			shortcut_jump(prev, node))
			continue;
		}
		break;
	}
	prev = node;
	node = next;
    }
}

static void
_sequential_labels(jit_state_t *_jit)
{
    jit_node_t		 *jump;
    jit_node_t		 *link;
    jit_node_t		 *prev;
    jit_node_t		 *next;
    jit_node_t		 *node;

    for (prev = node = _jit->head; node; node = next) {
	next = node->next;
	if (node->code == jit_code_label) {
	    if (!node->flag) {
		if (!node->link) {
		    del_label(prev, node);
		    continue;
		}
		if (prev != node && prev->code == jit_code_label) {
		    if ((jump = node->link)) {
			for (; jump; jump = link) {
			    link = jump->link;
			    jump->u.n = prev;
			    jump->link = prev->link;
			    prev->link = jump;
			}
			node->link = null;
		    }
		    del_label(prev, node);
		    continue;
		}
	    }
	    if (next && next->code == jit_code_label && !next->flag) {
		if ((jump = next->link)) {
		    for (; jump; jump = link) {
			link = jump->link;
			jump->u.n = node;
			jump->link = node->link;
			node->link = jump;
		    }
		    next->link = null;
		}
		del_label(node, next);
		next = node->next;
		continue;
	    }
	}
	prev = node;
    }
}

static jit_bool_t
_shortcut_jump(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node)
{
    jit_bool_t		 cond;
    jit_node_t		*jump;
    jit_node_t		*next;
    jit_node_t		*temp;

    if (!(node->flag & jit_flag_node))
	return (false);
    cond = node->code != jit_code_jmpi && node->code != jit_code_jmpr;
    jump = node->u.n;
    for (next = jump->next; next; next = next->next) {
	switch (next->code) {
	    case jit_code_jmpi:
		if (jump->link == node)
		    jump->link = node->link;
		else {
		    for (temp = jump->link;
			 temp->link != node;
			 temp = temp->link)
			assert(temp != null);
		    temp->link = node->link;
		}
		jump = next->u.n;
		node->u.n = jump;
		node->link = jump->link;
		jump->link = node;
		return (true);
	    case jit_code_jmpr:
		if (cond)
		    return (false);
		node->code = jit_code_jmpr;
		node->u.w = next->u.w;
		node->link = null;
		return (true);
	    case jit_code_note:		case jit_code_label:
		break;
	    default:
		return (false);
	}
    }
    return (false);
}

static jit_bool_t
_redundant_jump(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node)
{
    jit_node_t		*local_prev;
    jit_node_t		*local_next;

    if (!(node->flag & jit_flag_node))
	return (false);
    for (local_prev = node, local_next = node->next;
	 local_next;
	 local_prev = local_next, local_next = local_next->next) {

	switch (local_next->code) {
	    case jit_code_label:	case jit_code_epilog:
		if (node->u.n == local_next) {
		    if (local_next->link == node)
			local_next->link = node->link;
		    else {
			for (local_prev = local_next->link;
			     local_prev->link != node;
			     local_prev = local_prev->link)
			    assert(local_prev != null);
			local_prev->link = node->link;
		    }
		    del_node(prev, node);
		    return (true);
		}
		break;
	    case jit_code_note:
		break;
	    default:
		return (false);
	}
    }
    return (false);
}

static jit_code_t
reverse_jump_code(jit_code_t code)
{
    switch (code) {
	case jit_code_bltr:	return (jit_code_bger);
	case jit_code_blti:	return (jit_code_bgei);
	case jit_code_bltr_u:	return (jit_code_bger_u);
	case jit_code_blti_u:	return (jit_code_bgei_u);
	case jit_code_bler:	return (jit_code_bgtr);
	case jit_code_blei:	return (jit_code_bgti);
	case jit_code_bler_u:	return (jit_code_bgtr_u);
	case jit_code_blei_u:	return (jit_code_bgti_u);
	case jit_code_beqr:	return (jit_code_bner);
	case jit_code_beqi:	return (jit_code_bnei);
	case jit_code_bger:	return (jit_code_bltr);
	case jit_code_bgei:	return (jit_code_blti);
	case jit_code_bger_u:	return (jit_code_bltr_u);
	case jit_code_bgei_u:	return (jit_code_blti_u);
	case jit_code_bgtr:	return (jit_code_bler);
	case jit_code_bgti:	return (jit_code_blei);
	case jit_code_bgtr_u:	return (jit_code_bler_u);
	case jit_code_bgti_u:	return (jit_code_blei_u);
	case jit_code_bner:	return (jit_code_beqr);
	case jit_code_bnei:	return (jit_code_beqi);
	case jit_code_bmsr:	return (jit_code_bmcr);
	case jit_code_bmsi:	return (jit_code_bmci);
	case jit_code_bmcr:	return (jit_code_bmsr);
	case jit_code_bmci:	return (jit_code_bmsi);
	case jit_code_bltr_f:	return (jit_code_bunger_f);
	case jit_code_blti_f:	return (jit_code_bungei_f);
	case jit_code_bler_f:	return (jit_code_bungtr_f);
	case jit_code_blei_f:	return (jit_code_bungti_f);
	case jit_code_beqr_f:	return (jit_code_bltgtr_f);
	case jit_code_beqi_f:	return (jit_code_bltgti_f);
	case jit_code_bger_f:	return (jit_code_bunltr_f);
	case jit_code_bgei_f:	return (jit_code_bunlti_f);
	case jit_code_bgtr_f:	return (jit_code_bunler_f);
	case jit_code_bgti_f:	return (jit_code_bunlei_f);
	case jit_code_bner_f:	return (jit_code_buneqr_f);
	case jit_code_bnei_f:	return (jit_code_buneqi_f);
	case jit_code_bunltr_f:	return (jit_code_bger_f);
	case jit_code_bunlti_f:	return (jit_code_bgei_f);
	case jit_code_bunler_f:	return (jit_code_bgtr_f);
	case jit_code_bunlei_f:	return (jit_code_bgti_f);
	case jit_code_buneqr_f:	return (jit_code_bgtr_f);
	case jit_code_buneqi_f:	return (jit_code_bgti_f);
	case jit_code_bunger_f:	return (jit_code_bltr_f);
	case jit_code_bungei_f:	return (jit_code_blti_f);
	case jit_code_bungtr_f:	return (jit_code_bler_f);
	case jit_code_bungti_f:	return (jit_code_blei_f);
	case jit_code_bltgtr_f:	return (jit_code_beqr_f);
	case jit_code_bltgti_f:	return (jit_code_beqi_f);
	case jit_code_bordr_f:	return (jit_code_bunordr_f);
	case jit_code_bordi_f:	return (jit_code_bunordi_f);
	case jit_code_bunordr_f:return (jit_code_bordr_f);
	case jit_code_bunordi_f:return (jit_code_bordi_f);
	case jit_code_bltr_d:	return (jit_code_bunger_d);
	case jit_code_blti_d:	return (jit_code_bungei_d);
	case jit_code_bler_d:	return (jit_code_bungtr_d);
	case jit_code_blei_d:	return (jit_code_bungti_d);
	case jit_code_beqr_d:	return (jit_code_bltgtr_d);
	case jit_code_beqi_d:	return (jit_code_bltgti_d);
	case jit_code_bger_d:	return (jit_code_bunltr_d);
	case jit_code_bgei_d:	return (jit_code_bunlti_d);
	case jit_code_bgtr_d:	return (jit_code_bunler_d);
	case jit_code_bgti_d:	return (jit_code_bunlei_d);
	case jit_code_bner_d:	return (jit_code_buneqr_d);
	case jit_code_bnei_d:	return (jit_code_buneqi_d);
	case jit_code_bunltr_d:	return (jit_code_bger_d);
	case jit_code_bunlti_d:	return (jit_code_bgei_d);
	case jit_code_bunler_d:	return (jit_code_bgtr_d);
	case jit_code_bunlei_d:	return (jit_code_bgti_d);
	case jit_code_buneqr_d:	return (jit_code_bgtr_d);
	case jit_code_buneqi_d:	return (jit_code_bgti_d);
	case jit_code_bunger_d:	return (jit_code_bltr_d);
	case jit_code_bungei_d:	return (jit_code_blti_d);
	case jit_code_bungtr_d:	return (jit_code_bler_d);
	case jit_code_bungti_d:	return (jit_code_blei_d);
	case jit_code_bltgtr_d:	return (jit_code_beqr_d);
	case jit_code_bltgti_d:	return (jit_code_beqi_d);
	case jit_code_bordr_d:	return (jit_code_bunordr_d);
	case jit_code_bordi_d:	return (jit_code_bunordi_d);
	case jit_code_bunordr_d:return (jit_code_bordr_d);
	case jit_code_bunordi_d:return (jit_code_bordi_d);
	case jit_code_boaddr:	return (jit_code_bxaddr);
	case jit_code_boaddi:	return (jit_code_bxaddi);
	case jit_code_boaddr_u:	return (jit_code_bxaddr_u);
	case jit_code_boaddi_u:	return (jit_code_bxaddi_u);
	case jit_code_bxaddr:	return (jit_code_boaddr);
	case jit_code_bxaddi:	return (jit_code_boaddi);
	case jit_code_bxaddr_u:	return (jit_code_boaddr_u);
	case jit_code_bxaddi_u:	return (jit_code_boaddi_u);
	case jit_code_bosubr:	return (jit_code_bxsubr);
	case jit_code_bosubi:	return (jit_code_bxsubi);
	case jit_code_bosubr_u:	return (jit_code_bxsubr_u);
	case jit_code_bosubi_u:	return (jit_code_bxsubi_u);
	case jit_code_bxsubr:	return (jit_code_bosubr);
	case jit_code_bxsubi:	return (jit_code_bosubi);
	case jit_code_bxsubr_u:	return (jit_code_bosubr_u);
	case jit_code_bxsubi_u:	return (jit_code_bosubi_u);
	default:		eerror("invalid jump code");
    }
}

/*
 * change common pattern:
 *	<cond_jump L0> <jump L1> <label L0>
 * into
 *	<reverse_cond_jump L1>
 */
static jit_bool_t
_reverse_jump(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node)
{
    jit_node_t		*local_prev;
    jit_node_t		*local_next;
    jit_node_t		*local_jump;

    if (!(node->flag & jit_flag_node))
	return (false);
    /* =><cond_jump L0> <jump L1> <label L0> */
    local_next = node->next;
    if (local_next->code != jit_code_jmpi)
	return (false);
    /* <cond_jump L0> =><jump L1> <label L0> */

    local_jump = local_next->u.n;
    for (local_prev = local_next, local_next = local_next->next;
	 local_next;
	 local_prev = local_next, local_next = local_next->next) {
	switch (local_next->code) {
	    case jit_code_label:	case jit_code_epilog:
		if (node->u.n == local_next) {
		    if (local_next->link == node)
			local_next->link = node->link;
		    else {
			for (local_prev = local_next->link;
			     local_prev->link != node;
			     local_prev = local_prev->link)
			    assert(local_prev != null);
			local_prev->link = node->link;
		    }
		    del_node(node, node->next);
		    node->code = reverse_jump_code(node->code);
		    node->u.n = local_jump;
		    node->link = local_jump->link;
		    local_jump->link = node;
		    return (true);
		}
		break;
	    case jit_code_note:
		break;
	    default:
		return (false);
	}
    }
    return (false);
}

static void
_redundant_store(jit_state_t *_jit, jit_node_t *node, jit_bool_t jump)
{
    jit_node_t		*iter;
    jit_node_t		*prev;
    jit_word_t		 word;
    jit_int32_t		 spec;
    jit_int32_t		 regno;

    if (jump) {
	prev = node->u.n;
	if (prev->code == jit_code_epilog)
	    return;
	assert(prev->code == jit_code_label);
	if ((prev->flag & jit_flag_head) || node->link || prev->link != node)
	    /* multiple sources */
	    return;
	/* if there are sequential labels it will return below */
    }
    else
	prev = node;
    word = node->w.w;
    regno = jit_regno(node->v.w);
    for (iter = prev->next; iter; prev = iter, iter = iter->next) {
	switch (iter->code) {
	    case jit_code_label:	case jit_code_prolog:
	    case jit_code_epilog:
		return;
	    case jit_code_movi:
		if (regno == jit_regno(iter->u.w)) {
		    if (iter->flag || iter->v.w != word)
			return;
		    del_node(prev, iter);
		    iter = prev;
		}
		break;
	    default:
		spec = jit_classify(iter->code);
		if ((spec & jit_cc_a0_jmp) ||
		    (((spec & (jit_cc_a0_reg|jit_cc_a0_chg)) ==
		      (jit_cc_a0_reg|jit_cc_a0_chg)) &&
		     regno == jit_regno(iter->u.w)) ||
		    (((spec & (jit_cc_a1_reg|jit_cc_a1_chg)) ==
		      (jit_cc_a1_reg|jit_cc_a1_chg)) &&
		     regno == jit_regno(iter->v.w)))
		    return;
		break;
	}
    }
}

static jit_bool_t
_simplify_movr(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node,
	       jit_int32_t kind, jit_int32_t size)
{
    jit_int32_t		 regno;
    jit_int32_t		 right;
    jit_value_t		*value;

    regno = jit_regno(node->u.w);
    right = jit_regno(node->v.w);
    value = _jit->values + regno;
    if ((value->kind == jit_kind_register &&
	 jit_regno(value->base.pair.l) == right &&
	 value->base.pair.h == _jit->gen[right]) ||
	(value->kind == kind && _jit->values[right].kind == kind &&
	 memcmp(&value->base.w, &_jit->values[right].base.w, size) == 0)) {
	del_node(prev, node);
	return (true);
    }
    if (_jit->values[right].kind == jit_kind_word)
	memcpy(value, _jit->values + right, sizeof(jit_value_t));
    else {
	value->kind = jit_kind_register;
	value->base.pair.l = right;
	value->base.pair.h = _jit->gen[regno];
    }
    ++_jit->gen[regno];

    return (false);
}

static jit_bool_t
_simplify_movi(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node,
	       jit_int32_t kind, jit_int32_t size)
{
    jit_value_t		*value;
    jit_int32_t		 spec;
    jit_int32_t		 regno;
    jit_int32_t		 offset;

    regno = jit_regno(node->u.w);
    value = _jit->values + regno;
    if (node->flag & jit_flag_node) {
	/* set to undefined if value will be patched */
	value->kind = 0;
	++_jit->gen[regno];
	return (false);
    }
    if (value->kind == kind) {
	if (memcmp(&node->v.w, &value->base.w, size) == 0) {
	    del_node(prev, node);
	    return (true);
	}
	spec = jit_class(_rvs[regno].spec);
	if (kind == jit_kind_word)
	    spec &= jit_class_gpr;
	else
	    spec &= (jit_class_xpr | jit_class_fpr);
	for (offset = 0; offset < _jit->reglen; offset++) {
	    if (_jit->values[offset].kind == kind &&
		memcmp(&node->v.w, &_jit->values[offset].base.w, size) == 0 &&
		(jit_class(_rvs[offset].spec) & spec) == spec) {
		if (kind == jit_kind_word)
		    node->code = jit_code_movr;
		else if (kind == jit_kind_float32)
		    node->code = jit_code_movr_f;
		else
		    node->code = jit_code_movr_d;
		node->v.w = offset;
		memcpy(value, _jit->values + offset, sizeof(jit_value_t));
		++_jit->gen[regno];
		return (false);
	    }
	}
    }
    value->kind = kind;
    memcpy(&value->base.w, &node->v.w, size);
    ++_jit->gen[regno];

    return (false);
}

/* simple/safe redundandy test not checking if another register
 * holds the same value
 */
static jit_bool_t
_simplify_ldxi(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node)
{
    jit_value_t		*value;
    jit_int32_t		 regno;
    jit_int32_t		 right;

    regno = jit_regno(node->u.w);
    right = jit_regno(node->v.w);
    value = _jit->values + regno;
    if (value->kind == jit_kind_code && value->code == node->code &&
	value->base.pair.l == right && value->base.pair.h == _jit->gen[right] &&
	node->w.w == value->disp.w) {
	del_node(prev, node);
	return (true);
    }
    value->kind = jit_kind_code;
    value->code = node->code;
    value->base.pair.l = right;
    value->base.pair.h = _jit->gen[right];
    value->disp.w = node->w.w;
    ++_jit->gen[regno];

    return (false);
}

static jit_bool_t
_simplify_stxi(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node)
{
    jit_value_t		*value;
    jit_int32_t		 regno;
    jit_int32_t		 right;
    jit_int32_t		 offset;

    regno = jit_regno(node->w.w);
    right = jit_regno(node->v.w);
    value = _jit->values + regno;

    /* check for redundant store after load */
    if (value->kind == jit_kind_code && value->code == node->code &&
	value->base.pair.l == right && value->base.pair.h == _jit->gen[right] &&
	node->w.w == value->disp.w) {
	del_node(prev, node);
	return (true);
    }

    /* assume anything can alias, and invalidate tracked values */
    for (offset = 0; offset < _jit->reglen; offset++) {
	if (_jit->values[offset].kind == jit_kind_code) {
	    _jit->values[offset].kind = 0;
	    ++_jit->gen[offset];
	}
    }

    /* no multiple information, so, if set to a constant,
     * prefer to keep that information */
    if (value->kind == 0) {
	value->kind = jit_kind_code;
	switch (node->code) {
	    /* no information about signed/unsigned either */
	    case jit_code_stxi_c:	value->code = jit_code_ldxi_c;	break;
	    case jit_code_stxi_s:	value->code = jit_code_ldxi_s;	break;
	    case jit_code_stxi_i:	value->code = jit_code_ldxi_i;	break;
	    case jit_code_stxi_l:	value->code = jit_code_ldxi_l;	break;
	    case jit_code_stxi_f:	value->code = jit_code_ldxi_f;	break;
	    case jit_code_stxi_d:	value->code = jit_code_ldxi_d;	break;
	    default:		 	abort();
	}
	value->kind = jit_kind_code;
	value->code = node->code;
	value->base.pair.l = right;
	value->base.pair.h = _jit->gen[right];
	value->disp.w = node->u.w;
    }

    return (false);
}

/* usually there should be only one store in the
 * jit_get_reg/jit_unget_reg, but properly handle
 * multiple ones by moving the save node */
static void
_simplify_spill(jit_state_t *_jit, jit_node_t *node, jit_int32_t regno)
{
    jit_node_t		*save;
    jit_node_t		*temp;

    if ((temp = _jit->spill[regno]) && (save = temp->next) != node) {
	temp->next = save->next;
	save->next = node->next;
	node->next = save;
	_jit->spill[regno] = node;
    }
}

/* checks for simple cases where a register is set more than
 * once to the same value, and is a common pattern of calls
 * to jit_pushargi and jit_pushargr
 */
static void
_simplify(jit_state_t *_jit)
{
    jit_node_t		*prev;
    jit_node_t		*node;
    jit_node_t		*next;
    jit_int32_t		 info;
    jit_int32_t		 regno;
    jit_int32_t		 offset;

    for (prev = null, node = _jit->head; node; prev = node, node = next) {
	next = node->next;
	switch (node->code) {
	    case jit_code_label:	case jit_code_prolog:
	    reset:
		memset(_jit->gen, 0, sizeof(jit_int32_t) * _jit->reglen);
		memset(_jit->values, 0, sizeof(jit_value_t) * _jit->reglen);
		break;
	    case jit_code_save:
		_jit->spill[jit_regno(node->u.w)] = prev;
		break;
	    case jit_code_load:
		regno = jit_regno(node->u.w);
		if (register_change_p(node->link->next, node, regno) !=
		    jit_reg_change) {
		    /* spill not required due to optimizing common
		     * redundancy case of calling jit_get_reg/jit_unget_reg
		     * and then setting the register to the value it is
		     * already holding */
		    patch_register(node->link->next, node,
				   jit_regno_patch|regno, regno);
		    del_node(_jit->spill[regno], node->link);
		    del_node(prev, node);
		    node = prev;
		}
		_jit->spill[regno] = null;
		break;
	    case jit_code_movr:
		regno = jit_regno(node->u.w);
		if (simplify_movr(prev, node,
				  jit_kind_word, sizeof(jit_word_t)))
		    simplify_spill(node = prev, regno);
		break;
	    case jit_code_movi:
		regno = jit_regno(node->u.w);
		if (simplify_movi(prev, node,
				  jit_kind_word, sizeof(jit_word_t)))
		    simplify_spill(node = prev, regno);
		break;
	    case jit_code_movr_f:
		regno = jit_regno(node->u.w);
		if (simplify_movr(prev, node,
				  jit_kind_float32, sizeof(jit_float32_t)))
		    simplify_spill(node = prev, regno);
		break;
	    case jit_code_movi_f:
		regno = jit_regno(node->u.w);
		if (simplify_movi(prev, node,
				  jit_kind_float32, sizeof(jit_float32_t)))
		    simplify_spill(node = prev, regno);
		break;
	    case jit_code_movr_d:
		regno = jit_regno(node->u.w);
		if (simplify_movr(prev, node,
				  jit_kind_float64, sizeof(jit_float64_t)))
		    simplify_spill(node = prev, regno);
		break;
	    case jit_code_movi_d:
		regno = jit_regno(node->u.w);
		if (simplify_movi(prev, node,
				  jit_kind_float64, sizeof(jit_float64_t)))
		    simplify_spill(node = prev, regno);
		break;
	    case jit_code_ldxi_c:	case jit_code_ldxi_uc:
	    case jit_code_ldxi_s:	case jit_code_ldxi_us:
	    case jit_code_ldxi_i:	case jit_code_ldxi_ui:
	    case jit_code_ldxi_l:
	    case jit_code_ldxi_f:	case jit_code_ldxi_d:
		regno = jit_regno(node->u.w);
		if (simplify_ldxi(prev, node))
		    simplify_spill(node = prev, regno);
		break;
	    case jit_code_stxi_c:	case jit_code_stxi_s:
	    case jit_code_stxi_i:	case jit_code_stxi_l:
	    case jit_code_stxi_f:	case jit_code_stxi_d:
		regno = jit_regno(node->u.w);
		if (simplify_stxi(prev, node))
		    simplify_spill(node = prev, regno);
		break;
	    case jit_code_callr:	case jit_code_calli:
		for (offset = 0; offset < _jit->reglen; offset++) {
		    if (!(jit_class(_rvs[offset].spec) & jit_class_sav)) {
			_jit->values[offset].kind = 0;
			++_jit->gen[offset];
		    }
		}
		break;
	    default:
		info = jit_classify(node->code);
		if (info & jit_cc_a0_jmp)
		    /* labels are not implicitly added when not taking
		     * a conditional branch */
		    goto reset;
		if (info & jit_cc_a0_chg) {
		    regno = jit_regno(node->u.w);
		    _jit->values[regno].kind = 0;
		    ++_jit->gen[regno];
		}
		if (info & jit_cc_a1_chg) {
		    regno = jit_regno(node->v.w);
		    _jit->values[regno].kind = 0;
		    ++_jit->gen[regno];
		}
		break;
	}
    }
}

static jit_int32_t
_register_change_p(jit_state_t *_jit, jit_node_t *node, jit_node_t *link,
		   jit_int32_t regno)
{
    jit_int32_t		value;

    for (; node != link; node = node->next) {
	switch (node->code) {
	    case jit_code_label:	case jit_code_prolog:
		/* lack of extra information so cannot say it is undefined */
		return (jit_reg_change);
	    case jit_code_callr:	case jit_code_calli:
		if (!(jit_class(_rvs[regno].spec) & jit_class_sav))
		    return (jit_reg_undef);
		break;
	    default:
		value = jit_classify(node->code);
		/* lack of extra information */
		if (value & jit_cc_a0_jmp)
		    return (jit_reg_change);
		else if ((value & jit_cc_a0_reg) && node->u.w == regno &&
			 (value & jit_cc_a0_chg))
		    return (jit_reg_change);
		else if ((value & jit_cc_a1_reg) && node->v.w == regno &&
			 (value & jit_cc_a1_chg))
		    return (jit_reg_change);
	}
    }

    return (jit_reg_static);
}

/* most of this could be done at the same time as generating jit, but
 * avoid complications on different cpu backends and patch spill/loads
 * here, by simulating jit generation */
static jit_bool_t
_spill_reglive_p(jit_state_t *_jit, jit_node_t *node, jit_int32_t regno)
{
    if (!jit_regset_tstbit(_jit->reglive, regno)) {
	mpz_set_ui(_jit->blockmask, 0);
	jit_regset_setbit(_jit->regmask, regno);
	jit_update(node->next, &_jit->reglive, &_jit->regmask);
	if (!jit_regset_tstbit(_jit->reglive, regno) &&
	    register_change_p(node->next, node->link, regno) != jit_reg_change)
	    return (false);
    }

    return (true);
}

static void
_patch_registers(jit_state_t *_jit)
{
    jit_node_t		*prev;
    jit_node_t		*node;
    jit_node_t		*next;
    jit_int32_t		 info;
    jit_int32_t		 spec;
    jit_int32_t		 regno;
    jit_int32_t		 value;
    jit_pointer_t	*functions;

    _jit->function = null;
    functions = _jit->functions->v.obj;

    jit_reglive_setup();
    for (prev = null, node = _jit->head; node; node = next) {
	next = node->next;

	info = jit_classify(node->code);
	jit_regarg_set(node, info);

	switch (node->code) {
	    case jit_code_save:
		regno = jit_regno(node->u.w);
		if (!spill_reglive_p(node, regno)) {
		    /* register is not live, just remove spill/reload */
		    jit_regarg_clr(node, info);
		    node->link->v.w = jit_regload_delete;
		    del_node(prev, node);
		    continue;
		}
		else {
		    /* try to find a free register of the same class */
		    spec = jit_class(_rvs[regno].spec) & ~jit_class_arg;
		    for (value = 0; value < _jit->reglen; value++) {
			if (value != regno &&
			    ((jit_class(_rvs[value].spec) & spec) &
			     ~jit_class_arg) == spec &&
			    !jit_regset_tstbit(_jit->regarg, value) &&
			    !spill_reglive_p(node, value))
			    break;
		    }
		    if (value < _jit->reglen) {
			jit_regarg_clr(node, info);
			patch_register(node->next, node->link,
				       jit_regno_patch|node->u.w,
				       jit_regno_patch|value);
			/* mark as live just in case there are nested
			 * register patches, so that next patch will
			 * not want to use the same register */
			jit_regset_setbit(_jit->reglive, value);
			/* register is not live, just remove spill/reload */
			node->link->v.w = jit_regload_isdead;
			del_node(prev, node);
			continue;
		    }
		    else {
			/* failed to find a free register */
			if (spec & jit_class_gpr) {
			    if (!_jit->function->regoff[regno])
				_jit->function->regoff[regno] =
				    jit_allocai(sizeof(jit_word_t));
#if __WORDSIZE == 32
			    node->code = jit_code_stxi_i;
#else
			    node->code = jit_code_stxi_l;
#endif
			}
			else {
			    node->code = jit_code_stxi_d;
			    if (!_jit->function->regoff[regno])
				_jit->function->regoff[regno] =
				    jit_allocai(sizeof(jit_float64_t));
			}
			node->u.w = _jit->function->regoff[regno];
			node->v.w = JIT_FP;
			node->w.w = regno;
			node->link = null;
		    }
		}
		break;
	    case jit_code_load:
		regno = jit_regno(node->u.w);
		if (node->v.w) {
		    if (node->v.w == jit_regload_isdead)
			jit_regset_clrbit(_jit->reglive, regno);
		    del_node(prev, node);
		    continue;
		}
		spec = jit_class(_rvs[regno].spec);
		if (spec & jit_class_gpr) {
#if __WORDSIZE == 32
		    node->code = jit_code_ldxi_i;
#else
		    node->code = jit_code_ldxi_l;
#endif
		}
		else
		    node->code = jit_code_ldxi_d;
		node->v.w = regno;
		node->v.w = JIT_FP;
		node->w.w = _jit->function->regoff[regno];
		node->link = null;
		break;
	    case jit_code_prolog:
		_jit->function = functions[node->u.w];
		break;
	    case jit_code_epilog:
		_jit->function = null;
		break;
	    default:
		break;
	}

	jit_regarg_clr(node, info);
	/* update register live state */
	jit_reglive(node);
	prev = node;
    }
}

static void
_patch_register(jit_state_t *_jit, jit_node_t *node, jit_node_t *link,
		jit_int32_t regno, jit_int32_t patch)
{
    jit_int32_t		value;

    for (; node != link; node = node->next) {
	value = jit_classify(node->code);
	if ((value & jit_cc_a0_reg) && node->u.w == regno)
	    node->u.w = patch;
	if ((value & jit_cc_a1_reg) && node->v.w == regno)
	    node->v.w = patch;
	if ((value & jit_cc_a2_reg) && node->w.w == regno)
	    node->w.w = patch;
    }
}

#if defined(__i386__) || defined(__x86_64__)
#  include "ejit_x86.c"
#elif defined(__mips__)
#  include "ejit_mips.c"
#elif defined(__arm__)
#  include "ejit_arm.c"
#elif defined(__ppc__)
#  include "ejit_ppc.c"
#endif

#if DEBUG
#  define print_chr(value)		eputc(std_output, value)
#  define print_hex(value)		eprint_int(std_output, &hexfmt, value)
#  define print_dec(value)		eprint_int(std_output, &decfmt, value)
#  define print_flt(value)		eprint_flt(std_output, &decfmt, value)
#  define print_str(value)		ewrite(std_output, value, strlen(value))
#  define print_ptr(value)		print_hex((jit_word_t)value)
#  define print_vec(value)		eprint_str(std_output, &decfmt, value)
#  define print_reg(value)						\
    do {								\
	if ((value) & jit_regno_patch)					\
	    print_chr('?');						\
	print_str(_rvs[jit_regno(value)].name);				\
    } while (0)
void
_jit_print(jit_state_t *_jit)
{
    jit_node_t		 *node;
    east_node_t		 *note;
    jit_block_t		 *block;
    ebool_t		  first;
    jit_int32_t		  value;
    eint32_t		  offset;
    jit_block_t		**blocks;
    eformat_t		  decfmt;
    eformat_t		  hexfmt;

    first = true;
    memset(&decfmt, 0, sizeof(eformat_t));
    decfmt.radix = 10;
    memset(&hexfmt, 0, sizeof(eformat_t));
    hexfmt.read = 1;
    hexfmt.radix = 16;
    blocks = _jit->blocks->v.obj;
    for (node = _jit->head; node; node = node->next) {
	if (!first)
	    print_chr('\n');
	else
	    first = false;
	if (node->code == jit_code_label ||
	    node->code == jit_code_prolog || node->code == jit_code_epilog) {
	    print_chr('L');
	    print_dec(node->v.w);
	    print_chr(':');
	    block = blocks[node->v.w];
	    for (offset = 0; offset < _jit->reglen; offset++) {
		if (jit_regset_tstbit(block->reglive, offset)) {
		    print_chr(' ');
		    print_reg(offset);
		}
	    }
	    continue;
	}
	value = jit_classify(node->code) &
	    (jit_cc_a0_int|jit_cc_a0_jmp|jit_cc_a0_reg|
	     jit_cc_a1_reg|jit_cc_a1_int|jit_cc_a1_flt|jit_cc_a1_dbl|
	     jit_cc_a2_reg|jit_cc_a2_int|jit_cc_a2_flt|jit_cc_a2_dbl);
	if (value & jit_cc_a0_jmp)
	    print_str("    ");
	else
	    print_chr('\t');
	ewrite(std_output,
	       _jit_code_info[node->code].name,
	       _jit_code_info[node->code].size);
	switch (node->code) {
	r:
	    print_chr(' ');		print_reg(node->u.w);	continue;
	n:
	    print_chr(' ');
	    if (!(node->flag & jit_flag_node))
		print_ptr(node->u.p);
	    else {
		print_chr('L');
		print_dec(node->u.n->v.w);
	    }
	    continue;
	r_r:
	    print_chr(' ');		print_reg(node->u.w);
	    print_chr(' ');		print_reg(node->v.w);	continue;
	r_w:
	    print_chr(' ');		print_reg(node->u.w);
	    print_chr(' ');		print_hex(node->v.w);	continue;
	r_f:
	    print_chr(' ');		print_reg(node->u.w);
	    print_chr(' ');
	    if (node->flag & jit_flag_data)
		print_flt(*(jit_float32_t *)node->v.n->u.w);
	    else
		print_flt(node->v.f);
	    continue;
	r_d:
	    print_chr(' ');		print_reg(node->u.w);
	    print_chr(' ');
	    if (node->flag & jit_flag_data)
		print_flt(*(jit_float64_t *)node->v.n->u.w);
	    else
		print_flt(node->v.d);
	    continue;
	w_r:
	    print_chr(' ');		print_hex(node->u.w);
	    print_chr(' ');		print_reg(node->v.w);	continue;
	r_r_r:
	    print_chr(' ');		print_reg(node->u.w);
	    print_chr(' ');		print_reg(node->v.w);
	    print_chr(' ');		print_reg(node->w.w);	continue;
	r_r_w:
	    print_chr(' ');		print_reg(node->u.w);
	    print_chr(' ');		print_reg(node->v.w);
	    print_chr(' ');		print_hex(node->w.w);	continue;
	r_r_f:
	    print_chr(' ');		print_reg(node->u.w);
	    print_chr(' ');		print_reg(node->v.w);
	    print_chr(' ');
	    if (node->flag & jit_flag_data)
		print_flt(*(jit_float32_t *)node->w.n->u.w);
	    else
		print_flt(node->w.f);
	    continue;
	r_r_d:
	    print_chr(' ');		print_reg(node->u.w);
	    print_chr(' ');		print_reg(node->v.w);
	    print_chr(' ');
	    if (node->flag & jit_flag_data)
		print_flt(*(jit_float64_t *)node->w.n->u.w);
	    else
		print_flt(node->w.d);
	    continue;
	w_r_r:
	    print_chr(' ');		print_hex(node->u.w);
	    print_chr(' ');		print_reg(node->v.w);
	    print_chr(' ');		print_reg(node->w.w);	continue;
	n_r_r:
	    print_chr(' ');
	    if (!(node->flag & jit_flag_node))
		print_ptr(node->u.p);
	    else {
		print_chr('L');
		print_dec(node->u.n->v.w);
	    }
	    print_chr(' ');		print_reg(node->v.w);
	    print_chr(' ');		print_reg(node->w.w);	continue;
	n_r_w:
	    print_chr(' ');
	    if (!(node->flag & jit_flag_node))
		print_ptr(node->u.p);
	    else {
		print_chr('L');
		print_dec(node->u.n->v.w);
	    }
	    print_chr(' ');		print_reg(node->v.w);
	    print_chr(' ');		print_hex(node->w.w);	continue;
	n_r_f:
	    print_chr(' ');
	    if (!(node->flag & jit_flag_node))
		print_ptr(node->u.p);
	    else{
		print_chr('L');
		print_dec(node->u.n->v.w);
	    }
	    print_chr(' ');		print_reg(node->v.w);
	    print_chr(' ');
	    if (node->flag & jit_flag_data)
		print_flt(*(jit_float32_t *)node->w.n->u.w);
	    else
		print_flt(node->w.f);
	    continue;
	n_r_d:
	    print_chr(' ');
	    if (!(node->flag & jit_flag_node))
		print_ptr(node->u.p);
	    else {
		print_chr('L');
		print_dec(node->u.n->v.w);
	    }
	    print_chr(' ');		print_reg(node->v.w);
	    print_chr(' ');
	    if (node->flag & jit_flag_data)
		print_flt(*(jit_float64_t *)node->w.n->u.w);
	    else
		print_flt(node->w.d);
	    continue;

	    case jit_code_note:
		if ((note = node->v.p)) {
		    print_chr(' ');
		    print_vec(note->cnote.name);
		    print_chr(':');
		    print_dec(note->cnote.line);
		}
		break;

	    case jit_code_data:
	    case jit_code_label:
	    case jit_code_prolog:	case jit_code_epilog:
		break;
	    case jit_code_save:		case jit_code_load:
		goto r;
	    default:
		switch (value) {
		    case jit_cc_a0_reg:
		    case jit_cc_a0_reg|jit_cc_a0_chg:
		    case jit_cc_a0_reg|jit_cc_a0_jmp:
			goto r;
		    case jit_cc_a0_jmp:
			goto n;
		    case jit_cc_a0_reg|jit_cc_a1_reg:
			goto r_r;
		    case jit_cc_a0_reg|jit_cc_a1_int:
			goto r_w;
		    case jit_cc_a0_reg|jit_cc_a1_flt:
			goto r_f;
		    case jit_cc_a0_reg|jit_cc_a1_dbl:
			goto r_d;
		    case jit_cc_a0_int|jit_cc_a1_reg:
			goto w_r;
		    case jit_cc_a0_reg|jit_cc_a1_reg|jit_cc_a2_reg:
			goto r_r_r;
		    case jit_cc_a0_reg|jit_cc_a1_reg|jit_cc_a2_int:
			goto r_r_w;
		    case jit_cc_a0_reg|jit_cc_a1_reg|jit_cc_a2_flt:
			goto r_r_f;
		    case jit_cc_a0_reg|jit_cc_a1_reg|jit_cc_a2_dbl:
			goto r_r_d;
		    case jit_cc_a0_int|jit_cc_a1_reg|jit_cc_a2_reg:
			goto w_r_r;
		    case jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a2_reg:
			goto n_r_r;
		    case jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a2_int:
			goto n_r_w;
		    case jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a2_flt:
			goto n_r_f;
		    case jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a2_dbl:
			goto n_r_d;
		    default:
			abort();
		}
		break;
	}
    }
    print_chr('\n');
    eflush(std_output);
}
#endif
